﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using ResxHelper.Logic;
using ResxHelper.Logic.Readers;

namespace ResxHelper.Object
{
    [Serializable]
    public class ResourceFile
    {
        public string FilePath { get; set; }
        public string FileName
        {
            get { return Path.GetFileName(FilePath); }
        }
        public CultureInfo CultureInfo { get; set; }
        public ResourceFileEntryCollection Entries { get; set; } 

        public ResourceFile()
        {
            Entries = new ResourceFileEntryCollection();
        }

        /// <summary>
        /// Returns the file name of this instance of the resourc object without the extension
        /// </summary>
        /// <returns></returns>
        public string GetFileNameWithoutExtension()
        {
            return Path.GetFileNameWithoutExtension(FilePath);
        }

        /// <summary>
        /// Creates a deep clone of this instance of the resource object
        /// </summary>
        /// <returns></returns>
        public ResourceFile Clone()
        {
            var clone = new ResourceFile();
            clone.FilePath = FilePath;
            foreach (var entry in Entries)
            {
                clone.Entries.Add(entry.Key, entry.Value.Clone());
            }
            return clone;
        }

        #region -- Static Methods --

        /// <summary>
        /// Opens the selected file and parses it into a resource file object
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static ResourceFile Open(string path)
        {
            var type = ResourceType(path);
            IResourceFileReader reader;
            switch (type)
            {
                case Object.ResourceType.Csv:
                    reader = ResourceFileReaderFactory.GetReader(path, type);
                    break;
                case Object.ResourceType.Resx:
                    reader = ResourceFileReaderFactory.GetReader(path, type);
                    break;
                case Object.ResourceType.Excel:
                    reader = ResourceFileReaderFactory.GetReader(path, type);
                    break;
                default:
                    throw new FileLoadException("Unsupported File Type");
            }

            return reader.Read();
        }

        /// <summary>
        /// Attempts to find all related files within the same directory as the selected file based on the filename
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static List<ResourceFile> GetRelatedResourceFiles(string path)
        {
            var related = FindRelated(path);

            return related.Select(relatedPath =>
                ResourceFileReaderFactory.GetReader(relatedPath, Object.ResourceType.Resx))
                    .Select(reader => reader.Read())
                    .ToList();
        }

        /// <summary>
        /// Attempts to find the paths for all related files within the same directory as the selected file based on the filename
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static string[] FindRelated(string path)
        {
            var directoryName = Path.GetDirectoryName(path);
            if (directoryName == null) return null;

            var directory = new DirectoryInfo(directoryName);
            var files = directory
                .GetFiles(String.Format("{0}*.resx", GetInvariantFileName(path)), SearchOption.AllDirectories)
                .Where(f => f.FullName != path);

            return files.Select(file => file.FullName).ToArray();
        }

        /// <summary>
        /// Attempts to get the name of the invariant file based on the selected file path
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public static string GetInvariantFileName(string filePath)
        {
            if (String.IsNullOrEmpty(filePath)) return String.Empty;

            var fileName = Path.GetFileNameWithoutExtension(filePath);

            if (fileName == null) return null;
            var split = fileName.Split(new[] { "." }, StringSplitOptions.None);
            return split[0];
        }

        /// <summary>
        /// Attempts to find the path of the invariant file based on the file name
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public static string GetInvariantFilePath(string filePath)
        {
            var directory = Path.GetDirectoryName(filePath);
            var ext = Path.GetExtension(filePath);
            return String.Format("{0}\\{1}{2}", directory, GetInvariantFileName(filePath), ext);
        }

        /// <summary>
        /// Returns a list including the requested file and all related files
        /// </summary>
        /// <param name="path"></param>
        /// <param name="includeInvariant"></param>
        /// <returns></returns>
        public static List<ResourceFile> GetAllResourceFiles(string path, bool includeInvariant = true)
        {
            var files = new List<ResourceFile>();
            var reader = ResourceFileReaderFactory.GetReader(path, Object.ResourceType.Resx);

            files.Add(reader.Read());
            files.AddRange(GetRelatedResourceFiles(path));

            if (!includeInvariant)
            {
                var invariant = files.SingleOrDefault(f => f.CultureInfo.Equals(CultureInfo.InvariantCulture));
                files.Remove(invariant);
            }
            return files;
        }

        /// <summary>
        /// Returns a list of related files only
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public static CultureInfo ReadCulture(string filePath)
        {
            if (String.IsNullOrEmpty(filePath)) return CultureInfo.InvariantCulture;

            var fileName = Path.GetFileNameWithoutExtension(filePath);
            if (fileName == null) return null;

            var split = fileName.Split(new[] { "." }, StringSplitOptions.None);

            return split.Count() < 2
                       ? CultureInfo.InvariantCulture
                       : new CultureInfo(split.Last());
        }

        /// <summary>
        /// Figures out what the resource type is based on the file extension
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static ResourceType ResourceType(string path)
        {
            var ext = Path.GetExtension(path);
            if (String.IsNullOrEmpty(ext)) return Object.ResourceType.Csv;

            ResourceType type;
            switch (ext)
            {
                case ".csv":
                    type = Object.ResourceType.Csv;
                    break;
                case ".resx":
                    type = Object.ResourceType.Resx;
                    break;
                case ".xls":
                    type = Object.ResourceType.Excel;
                    break;
                default:
                    throw new FileLoadException(String.Format("Unsupported File Type {0}", ext));
            }

            return type;
        }

        /// <summary>
        /// Figures out what the extension should be based on the resource type
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static string GetExtension(ResourceType type)
        {
            var ext = String.Empty;
            switch (type)
            {
                case Object.ResourceType.Resx:
                    ext = "resx";
                    break;
                case Object.ResourceType.Excel:
                    ext = "xls";
                    break;
                case Object.ResourceType.Csv:
                    ext = "csv";
                    break;
            }
            return ext;
        }

        #endregion
    }
}
